package com.pianzhu.blockchainemr.utils;

import java.sql.*;
import java.util.*;
import java.io.*;

/**
 * 增强版实体类生成器 ，支持JSON类型字段
 */
public class PojoGenerator {

    // 输出目录
    private static final String OUTPUT_DIR = "src/main/java/com/pianzhu/blockchainemr/pojo/";

    // 包名
    private static final String PACKAGE_NAME = "com.pianzhu.blockchainemr.pojo";

    // 数据库配置
    private static final String DATABASE_URL = "jdbc:mysql://localhost:3306/blockchain_emr";
    private static final String DATABASE_USERNAME = "root";
    private static final String DATABASE_PASSWORD = "root";

    // JSON类型映射
    private static final Set<String> JSON_TYPES = new HashSet<>(Arrays.asList(
            "json", "jsonb", "longtext"
    ));

    public static void main(String[] args) {
        generateAllEntities();
    }

    /**
     * 生成blockchain_emr数据库的所有表的实体类
     */
    public static void generateAllEntities() {
        Connection conn = null;
        try {
            // 创建输出目录
            createOutputDirectory();

            // 获取数据库连接
            conn = DriverManager.getConnection(DATABASE_URL, DATABASE_USERNAME, DATABASE_PASSWORD);

            // 获取blockchain_emr数据库的所有表
            List<TableInfo> tables = getAllTables(conn);

            System.out.println("在 blockchain_emr 数据库中找到 " + tables.size() + " 个表:");
            for (TableInfo table : tables) {
                System.out.println("  - " + table.getName() + " : " + table.getComment());
            }

            // 为每个表生成实体类
            for (TableInfo table : tables) {
                generateEntity(conn, table.getName(), table.getComment());
            }

            System.out.println("所有实体类生成完成！共生成 " + tables.size() + " 个实体类。");

        } catch (Exception e) {
            System.err.println("生成实体类时发生错误: " + e.getMessage());
            e.printStackTrace();
        } finally {
            closeConnection(conn);
        }
    }

    /**
     * 生成单个表的实体类
     */
    public static void generateEntity(String tableName, String tableComment) {
        Connection conn = null;
        try {
            createOutputDirectory();
            conn = DriverManager.getConnection(DATABASE_URL, DATABASE_USERNAME, DATABASE_PASSWORD);
            generateEntity(conn, tableName, tableComment);
            System.out.println("成功生成实体类: " + tableName);
        } catch (Exception e) {
            System.err.println("生成表 " + tableName + " 的实体类时发生错误: " + e.getMessage());
            e.printStackTrace();
        } finally {
            closeConnection(conn);
        }
    }

    /**
     * 生成实体类的核心方法
     */
    private static void generateEntity(Connection conn, String tableName, String tableComment) throws Exception {
        // 获取表的所有列信息
        List<ColumnInfo> columns = getTableColumns(conn, tableName);

        // 生成类名（驼峰命名）
        String className = toCamelCase(tableName, true);

        // 构建Java代码
        StringBuilder javaCode = new StringBuilder();

        // 包声明和导入
        javaCode.append("package ").append(PACKAGE_NAME).append(";\n\n");
        javaCode.append("import lombok.Data;\n");
        javaCode.append("import java.util.*;\n");
        javaCode.append("import java.time.*;\n");
        javaCode.append("import com.fasterxml.jackson.annotation.JsonFormat;\n");
        javaCode.append("import com.fasterxml.jackson.annotation.JsonProperty;\n");
        javaCode.append("import com.fasterxml.jackson.core.type.TypeReference;\n");
        javaCode.append("import com.fasterxml.jackson.databind.ObjectMapper;\n");
        javaCode.append("import java.io.IOException;\n\n");

        // 类注释
        javaCode.append("/**\n");
        javaCode.append(" * ").append(tableComment.isEmpty() ? className + "实体类" : tableComment).append("\n");
        javaCode.append(" * 对应数据库表: ").append(tableName).append("\n");
        javaCode.append(" * 数据库: blockchain_emr\n");
        javaCode.append(" * 自动生成 - 请勿手动修改\n");
        javaCode.append(" */\n");

        // 类声明
        javaCode.append("@Data\n");
        javaCode.append("public class ").append(className).append(" {\n\n");

        // 字段声明
        for (ColumnInfo column : columns) {
            javaCode.append("    /**\n");
            javaCode.append("     * ").append(column.getComment()).append("\n");
            javaCode.append("     * 字段类型: ").append(column.getTypeName()).append("\n");
            javaCode.append("     */\n");

            // 如果是JSON字段，添加JsonProperty注解
            if (column.isJsonType()) {
                javaCode.append("    @JsonProperty(\"").append(column.getName()).append("\")\n");
            }

            // 如果是日期字段，添加JsonFormat注解
            if (column.isDateType()) {
                javaCode.append("    @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\")\n");
            }

            javaCode.append("    private ").append(column.getJavaType()).append(" ")
                    .append(toCamelCase(column.getName(), false)).append(";\n\n");
        }

        // JSON字段的处理方法
        javaCode.append(generateJsonMethods(columns, className));

        javaCode.append("}\n");

        // 写入文件
        writeToFile(className + ".java", javaCode.toString());

        System.out.println("生成实体类: " + className + " (表: " + tableName + ") - 包含 " + columns.size() + " 个字段");
    }

    /**
     * 生成JSON字段的处理方法
     */
    private static String generateJsonMethods(List<ColumnInfo> columns, String className) {
        StringBuilder methods = new StringBuilder();

        for (ColumnInfo column : columns) {
            if (column.isJsonType()) {
                String fieldName = toCamelCase(column.getName(), false);
                String methodSuffix = toCamelCase(column.getName(), true);

                // JSON字符串转对象的方法
                methods.append("    /**\n");
                methods.append("     * 将JSON字符串解析为").append(column.getJavaType()).append("对象\n");
                methods.append("     * 用于处理数据库中的JSON字段: ").append(column.getName()).append("\n");
                methods.append("     */\n");
                methods.append("    public static ").append(column.getJavaType())
                        .append(" parse").append(methodSuffix).append("(String json) {\n");
                methods.append("        if (json == null || json.trim().isEmpty()) {\n");
                methods.append("            return null;\n");
                methods.append("        }\n");
                methods.append("        try {\n");
                methods.append("            ObjectMapper mapper = new ObjectMapper();\n");
                methods.append("            return mapper.readValue(json, new TypeReference<").append(column.getJavaType()).append(">(){});\n");
                methods.append("        } catch (IOException e) {\n");
                methods.append("            throw new RuntimeException(\"解析JSON字段 ").append(column.getName()).append(" 失败: \" + json, e);\n");
                methods.append("        }\n");
                methods.append("    }\n\n");

                // 对象转JSON字符串的方法
                methods.append("    /**\n");
                methods.append("     * 将").append(column.getJavaType()).append("对象序列化为JSON字符串\n");
                methods.append("     * 用于存储到数据库JSON字段: ").append(column.getName()).append("\n");
                methods.append("     */\n");
                methods.append("    public static String serialize").append(methodSuffix).append("(")
                        .append(column.getJavaType()).append(" obj) {\n");
                methods.append("        if (obj == null) {\n");
                methods.append("            return null;\n");
                methods.append("        }\n");
                methods.append("        try {\n");
                methods.append("            ObjectMapper mapper = new ObjectMapper();\n");
                methods.append("            return mapper.writeValueAsString(obj);\n");
                methods.append("        } catch (IOException e) {\n");
                methods.append("            throw new RuntimeException(\"序列化JSON字段 ").append(column.getName()).append(" 失败\", e);\n");
                methods.append("        }\n");
                methods.append("    }\n\n");
            }
        }

        return methods.toString();
    }

    /**
     * 获取blockchain_emr数据库的所有表信息
     */
    private static List<TableInfo> getAllTables(Connection conn) throws SQLException {
        List<TableInfo> tables = new ArrayList<>();
        DatabaseMetaData metaData = conn.getMetaData();

        // 只查询blockchain_emr数据库中的表
        ResultSet rs = metaData.getTables("blockchain_emr", null, "%", new String[]{"TABLE"});
        while (rs.next()) {
            String tableName = rs.getString("TABLE_NAME");
            String tableComment = rs.getString("REMARKS");
            tables.add(new TableInfo(tableName, tableComment != null ? tableComment : ""));
        }
        rs.close();

        return tables;
    }

    /**
     * 获取表的列信息
     */
    private static List<ColumnInfo> getTableColumns(Connection conn, String tableName) throws SQLException {
        List<ColumnInfo> columns = new ArrayList<>();
        DatabaseMetaData metaData = conn.getMetaData();

        // 查询指定表的列信息
        ResultSet rs = metaData.getColumns("blockchain_emr", null, tableName, "%");
        while (rs.next()) {
            String columnName = rs.getString("COLUMN_NAME");
            String typeName = rs.getString("TYPE_NAME").toLowerCase();
            int dataType = rs.getInt("DATA_TYPE");
            String remarks = rs.getString("REMARKS");
            int columnSize = rs.getInt("COLUMN_SIZE");
            int decimalDigits = rs.getInt("DECIMAL_DIGITS");
            boolean isNullable = "YES".equals(rs.getString("IS_NULLABLE"));

            columns.add(new ColumnInfo(columnName, typeName, dataType, remarks,
                    columnSize, decimalDigits, isNullable));
        }
        rs.close();

        return columns;
    }

    /**
     * 转换为驼峰命名
     */
    private static String toCamelCase(String name, boolean firstLetterUpper) {
        if (name == null || name.isEmpty()) {
            return name;
        }

        String[] parts = name.split("_");
        StringBuilder result = new StringBuilder();

        for (int i = 0; i < parts.length; i++) {
            String part = parts[i];
            if (!part.isEmpty()) {
                if (i == 0 && !firstLetterUpper) {
                    result.append(part.toLowerCase());
                } else {
                    result.append(Character.toUpperCase(part.charAt(0)))
                            .append(part.substring(1).toLowerCase());
                }
            }
        }

        return result.toString();
    }

    /**
     * 创建输出目录
     */
    private static void createOutputDirectory() {
        File dir = new File(OUTPUT_DIR);
        if (!dir.exists()) {
            boolean created = dir.mkdirs();
            if (created) {
                System.out.println("创建输出目录: " + OUTPUT_DIR);
            }
        }
    }

    /**
     * 写入文件
     */
    private static void writeToFile(String fileName, String content) throws IOException {
        File file = new File(OUTPUT_DIR + fileName);
        try (FileWriter writer = new FileWriter(file)) {
            writer.write(content);
        }
    }

    /**
     * 关闭数据库连接
     */
    private static void closeConnection(Connection conn) {
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                System.err.println("关闭数据库连接时发生错误: " + e.getMessage());
            }
        }
    }

    /**
     * 表信息类
     */
    static class TableInfo {
        private String name;
        private String comment;

        public TableInfo(String name, String comment) {
            this.name = name;
            this.comment = comment;
        }

        public String getName() { return name; }
        public String getComment() { return comment; }
    }

    /**
     * 列信息类
     */
    static class ColumnInfo {
        private String name;
        private String typeName;
        private int dataType;
        private String comment;
        private int columnSize;
        private int decimalDigits;
        private boolean nullable;

        public ColumnInfo(String name, String typeName, int dataType, String comment,
                          int columnSize, int decimalDigits, boolean nullable) {
            this.name = name;
            this.typeName = typeName;
            this.dataType = dataType;
            this.comment = comment != null ? comment : "";
            this.columnSize = columnSize;
            this.decimalDigits = decimalDigits;
            this.nullable = nullable;
        }

        public String getName() { return name; }
        public String getTypeName() { return typeName; }
        public String getComment() { return comment; }

        /**
         * 获取Java类型
         */
        public String getJavaType() {
            // JSON类型处理
            if (isJsonType()) {
                return "Object";
            }

            switch (dataType) {
                case Types.BIT:
                case Types.BOOLEAN:
                    return "Boolean";
                case Types.TINYINT:
                case Types.SMALLINT:
                    return "Integer";
                case Types.INTEGER:
                    return nullable ? "Integer" : "int";
                case Types.BIGINT:
                    return nullable ? "Long" : "long";
                case Types.FLOAT:
                case Types.REAL:
                    return nullable ? "Float" : "float";
                case Types.DOUBLE:
                    return nullable ? "Double" : "double";
                case Types.DECIMAL:
                case Types.NUMERIC:
                    return "java.math.BigDecimal";
                case Types.CHAR:
                case Types.VARCHAR:
                case Types.LONGVARCHAR:
                case Types.NCHAR:
                case Types.NVARCHAR:
                case Types.LONGNVARCHAR:
                    return "String";
                case Types.DATE:
                    return "java.time.LocalDate";
                case Types.TIME:
                    return "java.time.LocalTime";
                case Types.TIMESTAMP:
                case Types.TIME_WITH_TIMEZONE:
                case Types.TIMESTAMP_WITH_TIMEZONE:
                    return "java.time.LocalDateTime";
                case Types.BLOB:
                case Types.BINARY:
                case Types.VARBINARY:
                case Types.LONGVARBINARY:
                    return "byte[]";
                case Types.CLOB:
                case Types.NCLOB:
                    return "String";
                default:
                    return "Object";
            }
        }

        /**
         * 判断是否为JSON类型
         */
        public boolean isJsonType() {
            return JSON_TYPES.contains(typeName.toLowerCase());
        }

        /**
         * 判断是否为日期类型
         */
        public boolean isDateType() {
            return dataType == Types.DATE || dataType == Types.TIME ||
                    dataType == Types.TIMESTAMP || dataType == Types.TIME_WITH_TIMEZONE ||
                    dataType == Types.TIMESTAMP_WITH_TIMEZONE;
        }
    }
}